home *** CD-ROM | disk | FTP | other *** search
- /* Activity is a formal class to group common behavior among
- tasks and milestones for projects. Activities know how to
- determine if they are on the critical path and keep track
- of their earlyStart, lateFinish and slack time.
- The user can set an earlyStart or lateFinish to override
- what is calculated.
-
- Since Activity descends from Node, it inherits all of the
- methods and instance variables from Node, e.g. name, desc,
- inputs, outputs, etc.
- */!!
-
- inherit(Node, #Activity, #(earlyStart /* calculated */
- lateFinish /* calculated */
- userEarlyStart /* user entered */
- userLateFinish /* user entered */
- slack /* slack time */
- critical /* boolean */), 2, nil)!!
-
- now(ActivityClass)!!
-
- now(Activity)!!
-
- /* Return the class name. Generic version.
- Ancestors will redefine this to use resources. */
- Def className(self)
- {
- ^asString(class(self));
- }!!
-
- /* Return true if the activity is "complete", e.g.
- connected. Descendants may redefine this.
- This is for experimental use only. */
- Def complete(self)
- {
- ^size(inputs) + size(outputs) > 0;
- }!!
-
- /* In general, activities place no limit on the number
- of connections. Descendants, such as Task, will
- redefine this to place limits. */
- Def maxConnectionNames(self, names)
- {
- ^names;
- }!!
-
- /* Delete an activity. First check to see if it is
- connected to anything and check with the user. */
- Def delete(self)
- {
- if size(inputs) + size(outputs) = 0
- cor yesNoBox(loadString(PW_WARNING),
- name + loadString(PW_ACTUSE1) +
- loadString(PW_ACTUSE2) + CR_LF +
- loadString(PW_DELETE)) == IDYES
- do(inputs,
- {using(input)
- disconnect(input, self);
- });
- do(outputs,
- {using(output)
- disconnect(self, output);
- });
- removeNode(network, self);
- remove(network.display, pos(self));
- endif;
- }!!
-
- /* Parse a string of nodeNames and return a set of
- nodes. Checks to see if they exist. */
- Def parseNodeNames(self, nodeNames | names, nodes, aNode)
- {
- names := words(nodeNames);
- names := maxConnectionNames(self, names);
- nodes := new(OrderedCollection, 5);
- do(names,
- {using(name)
- if aNode := checkNodeExists(network, name)
- add(nodes, aNode);
- endif;
- });
- ^nodes;
- }!!
-
- /* Checks the connections and reconnects if necessary.
- If the connections change, we may have to recalc
- the entire project. */
- Def checkConnection(self, inputsString, outputsString
- | newInputs, newOutputs, calcReqd)
- {
- if inputsString <> getInputNames(self)
- newInputs := parseNodeNames(self, inputsString);
- do(inputs,
- {using(input)
- if not(input in newInputs)
- disconnect(network, input.name, self.name);
- calcReqd := true;
- endif;
- });
- do(newInputs,
- {using(input)
- if not(input in inputs)
- connect(network, input.name, self.name);
- calcReqd := true;
- endif;
- });
- endif;
-
- if outputsString <> getOutputNames(self)
- newOutputs := parseNodeNames(self, outputsString);
- do(outputs,
- {using(output)
- if not(output in newOutputs)
- disconnect(network, self.name, output.name);
- calcReqd := true;
- endif
- });
- do(newOutputs,
- {using(output)
- if not(output in outputs)
- connect(network, self.name, output.name);
- calcReqd := true;
- endif;
- });
- endif;
-
- if autoCalc(network) cand calcReqd
- recalc(network)
- endif;
- }!!
-
- /* Return a string of output names.
- This is useful when editing. */
- Def getOutputNames(self | str)
- {
- str := "";
- do(outputs,
- {using(output) str := str + getName(output) + " ";
- });
- ^str;
- }!!
-
- /* Return a string of input names.
- This is useful when editing. */
- Def getInputNames(self | str)
- {
- str := "";
- do(inputs,
- {using(input) str := str + getName(input) + " ";
- });
- ^str;
- }!!
-
- /* Display and edit the activity information.
- Descendants that use this should have a dialogClass()
- method that returns the dialog class to be used. The
- dialog class should define methods run() and setEditItem().
- */
- Def editInfo(self | dlg, retValue)
- {
- showWaitCurs(); /* this can take a while */
- dlg := new(dialogClass(self));
- setEditItem(dlg, self);
- retValue := run(dlg, ThePort);
- showOldCurs(); /* all done */
- ^retValue;
- }!!
-
- /* In the simplest case an activity is critical if slack = 0.
- However, if the user has set the lateFinish, it could
- introduce excess slack to the project. Therefore, a node
- is critical if its slack is equal to the excess slack. */
- Def calcCritical(self)
- {
- critical := (slack = getSlack(network));
- }!!
-
- /* Set the values of an activity.
- Values is an array of name, desc,
- userEarlyStart, userLateFinish. */
- Def setValues(self, values | oldUES, oldULF)
- {
- oldUES := userEarlyStart;
- oldULF := userLateFinish;
- name := values[0];
- desc := values[1];
- userEarlyStart := values[2];
- userLateFinish := values[3];
- if autoCalc(network) cand
- (oldUES <> userEarlyStart cor
- oldULF <> userLateFinish)
- recalc(self);
- endif;
- }!!
-
- /* Get the user set earlyStart time. */
- Def getUserEarlyStart(self)
- {
- ^userEarlyStart;
- }!!
-
- /* Get the user set lateFinish time. */
- Def getUserLateFinish(self)
- {
- ^userLateFinish;
- }!!
-
- /* Return a string to be used as a caption in a window. */
- Def makeCaption(self)
- {
- ^className(self) + ": "+getName(self)+
- if critical(self)
- loadString(PW_CRITICAL)
- else
- loadString(PW_NONCRITICAL)
- endif;
- }!!
-
- /* Summarize useful information on a single line. */
- Def getInfoLine(self | str)
- {
- if critical(self)
- str := "*";
- else
- str := " ";
- endif;
- ^str + field(name, 8)
- + field(className(self), 4) + " "
- + field(asString(getCost(self)), 3) + " "
- + field(asString(getTime(self)), 2) + " "
- + field(asString(getSlack(self)), 3) + " "
- + field(asString(getEarlyStart(self)), 8) + " "
- + field(asString(getLateFinish(self)), 8);
- }!!
-
- /* Get the lateFinish time. At End, lateFinish = earlyStart,
- unless the user set a lateFinish. */
- Def getLateFinish(self)
- {
- if (size(outputs) == 0 and not(userLateFinish))
- lateFinish := earlyStart
- endif;
- ^lateFinish;
- }!!
-
- /* Calculate slack for an Activity. This is done
- on the backward recalc pass when ES, LF are
- up to date. Slack is never < 0. */
- Def calcSlack(self)
- {
- ^slack := max(0, getLateStart(self) - getEarlyStart(self));
- }!!
-
- /* Get the slack value. */
- Def getSlack(self)
- {
- ^slack;
- }!!
-
- /* Return the status of whether the node is critical or not. */
- Def critical(self)
- {
- ^critical;
- }!!
-
- /* Invalidate a node and recursively invalidate all
- nodes it's connected to. This is used to force
- an entire recalc of the network. */
- Def invalidate(self, visited)
- {
- reset(self); /* reset ivars */
- do(outputs, /* pass along */
- {using(elem)
- if not(elem in visited) /* avoid looping */
- add(visited, elem);
- invalidate(elem, visited); /* recurse */
- endif;
- });
- }!!
-
- /* Recalculate the network from this node onwards.
- This requires forcing a forward recalc1 and a
- backwards recalc2 from the end of the net. */
- Def recalc(self)
- {
- recalc1(self,true); /* force forward recalc1 */
- recalc2(network); /* do backwards recalc2 */
- }!!
-
- /* Backward recalc pass: (not optimized)
- Recalculate an activity's lateFinish then its slack and
- determine if it's critical.
- If the user has set a lateFinish, use it instead of
- recalculating. Always propogate recalc2 since a
- change to the time of a node will not change lateFinish,
- but it can change slack and critical, which are only
- known on the backwards pass.
-
- formula: LF = min(LF(i) - time(i)) for all outputs i
-
- Note: This could be optimized to only propogate recalc2
- as far back as the next Milestone. */
- Def recalc2(self)
- {
- if (userLateFinish)
- lateFinish := userLateFinish; /* user override */
- else
- lateFinish := asLong(date(12,31,1999));
- do(outputs,
- {using(output)
- lateFinish := min(lateFinish,
- getLateFinish(output) - getTime(output));
- });
- lateFinish := asDate(lateFinish);
- endif;
-
- calcSlack(self);
- calcCritical(self);
-
- /* Continue sending the recalc2 message. */
-
- do(inputs,
- {using(input)
- recalc2(input);
- });
- }!!
-
- /* Forward recalc pass: (optimized minimal recalc)
- Recalculate an activity's earlyStart. If the user has
- set an earlyStart, use it instead of recalculating.
- Send a message to the node's outputs to recalculate only
- if a forced recalc is required, or if earlyStart changes.
-
- formula: ES = max(ES(i) + time(i)) for all inputs i
-
- arguments:
- timeChanged: force a recalc1 if the time has changed
- */
- Def recalc1(self, timeChanged | oldEarlyStart)
- {
- oldEarlyStart := earlyStart;
-
- if (userEarlyStart)
- earlyStart := userEarlyStart; /* user override */
- else
- earlyStart := asLong(new(Date));
- do(inputs,
- {using(input)
- earlyStart := max(earlyStart,
- getEarlyStart(input) + getTime(input));
- });
- earlyStart := asDate(earlyStart);
- endif;
-
- /* Recalculate outputs only if earlyStart changed OR if time has
- changed. Don't force it to continue beyond the next level. */
-
- if timeChanged cor (earlyStart <> oldEarlyStart)
- do(outputs,
- {using(output) recalc1(output, nil);
- });
- endif;
- }!!
-
- /* Initialize a new Activity. */
- Def init(self)
- { init(self:Node); /* use ancestor init */
- reset(self); /* reset ivars */
- } !!
-
- /* Reset the instance variables. This is called by
- init and when an entire recalc is forced. */
- Def reset(self)
- {
- slack := 0;
- lateFinish := earlyStart := new(Date);
- critical := nil;
- }!!
-
- /* The user can enter a late finish date to be used
- instead of the calculated value. */
- Def setLateFinish(self, aDate)
- {
- userLateFinish := aDate;
- if autoCalc(network) /* just do backwards calc */
- recalc2(network);
- endif;
- }!!
-
- /* The user can enter an early start date to be used
- instead of the calculated value. */
- Def setEarlyStart(self, aDate)
- {
- userEarlyStart := aDate;
- if autoCalc(network)
- recalc(self);
- endif;
- } !!
-
- /* Get the earlyStart time. */
- Def getEarlyStart(self)
- {
- ^earlyStart;
- } !!
-
- /* Calculcate the earlyFinish time. */
- Def getEarlyFinish(self)
- {
- ^asDate(getEarlyStart(self) + getTime(self));
- }!!
-
- /* Calcualate the lateStart time. */
- Def getLateStart(self)
- {
- ^asDate(getLateFinish(self) - getTime(self));
- }!!
-
-
-